/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*************************************************************************
* File: buffer.hpp
*************************************************************************
*\author: starlove
*************************************************************************/

#ifndef __CC_PYTHON_BUFFER_H__
#define __CC_PYTHON_BUFFER_H__

#include "pool.hpp"
#include "memory.hpp"
#include <python_import_macro.h>

NS_CCPY_BEGIN

	class IAutoExpandBuffer
	{
	public:
		virtual ~IAutoExpandBuffer(){};
		virtual char *GetRawBuffer() = 0;
		virtual char *GetCurrPtr() = 0;
		virtual char *GetDataEnd() = 0;
		virtual char *GetPtr() = 0;
		virtual void SetOffset(unsigned int) = 0;
		virtual unsigned int GetSize() = 0;
		virtual unsigned int GetDataSize() = 0;
		virtual unsigned int GetRemainSize() = 0;
		virtual bool Align(unsigned int) = 0;
		virtual bool Expand(unsigned int) = 0;
		virtual void Reset(bool bClean=false) = 0;
		virtual bool CopyFrom(void *, unsigned int) = 0;
		virtual void Append(const void *, unsigned int) = 0;
		virtual void AppendChar(char) = 0;
		virtual void AppendStr(const char *) = 0;
		virtual bool IsDamaged() = 0;
		virtual void Discard() = 0;
	};

	class CBufferReader
	{
	public:		
		CBufferReader(IAutoExpandBuffer *pBuffer)
		{
			m_ptr_src = pBuffer->GetPtr();
			m_size = pBuffer->GetDataSize();
			Reset();
		}

		CBufferReader(const char *ptr, size_t size)
		{
			m_ptr_src = ptr;
			m_size = size;
			Reset();
		}

		void Reset(bool bClean = false)
		{
			m_ptr_curr = m_ptr_src;
			m_size_remain = m_size;
			if(bClean)
				memset((void *)m_ptr_src, 0, m_size);
		}

		const void *Peek()
		{
			return m_ptr_curr;
		}

		const void *Read(unsigned int size)
		{
			unsigned r_size = MIN(size, m_size);
			if (m_size_remain < r_size)
			{
				return NULL;
			}
			const void *p = m_ptr_curr;
			m_ptr_curr = CMemory<char>::MovePtr(m_ptr_curr, r_size);
			m_size_remain -= r_size;
			return p;
		}

		void Seek(int size)
		{
			unsigned s_size = m_size_remain - size;
			if(s_size < 0 || s_size > m_size)
			{
				return;
			}
			m_size_remain = s_size;
			m_ptr_curr = CMemory<char>::MovePtr(m_ptr_curr, size);
		}

		size_t GetRemainSize()
		{
			return m_size_remain;
		}

		size_t GetOffset()
		{
			return (size_t)(m_ptr_curr - m_ptr_src);
		}

		void *OffsetPeek(size_t s)
		{
			return CMemory<char>::MovePtr(m_ptr_src, s);
		}

		const void *Begin()
		{
			return m_ptr_src;
		}

	private:
		const char *m_ptr_curr;
		const char *m_ptr_src;
		unsigned int m_size_remain;
		unsigned int m_size;
	};

	template <int DEFSIZE>
	class CAutoExpandBuffer : public IAutoExpandBuffer
	{
	public:
		CAutoExpandBuffer()
			:m_szRawBuff(NULL)
			,m_szExpand(NULL)
			,m_curr(NULL)
			,m_ptr(NULL)
			,m_size(DEFSIZE)
			,m_bDamaged(false)
		{
			Reset();
		}

		~CAutoExpandBuffer()
		{
			if(m_szRawBuff)
				CSimplePool::GetInstance()->Free(m_szRawBuff);
			m_szExpand = NULL;
		}

		char *GetRawBuffer()
		{
			if(!m_szRawBuff)
				m_szRawBuff = (char *)CSimplePool::GetInstance()->Alloc(DEFSIZE);
			return m_szRawBuff;
		}

		char *GetCurrPtr()
		{
			return m_curr;
		}

		char *GetDataEnd()
		{
			return m_curr > m_ptr ? m_curr - 1: NULL;
		}

		char *GetPtr()
		{
			return m_ptr;
		}

		void SetOffset(unsigned int size)
		{
			if(size > m_size)
				return;
			m_curr += size;
		}

		unsigned int GetSize()
		{
			return m_size;
		}

		unsigned int GetDataSize()
		{
			return (m_curr - m_ptr);
		}

		unsigned int GetRemainSize()
		{
			return m_size - GetDataSize();
		}

		bool Align(unsigned int nSize)
		{
			if(nSize == 0)	return false;
			unsigned int ndata_size = (m_curr - m_ptr);
			if(ndata_size <= nSize)
			{
				Reset();
				return false;
			}
			else
			{
				unsigned int n_newsize = ndata_size - nSize;
				char *sz_new = (char *)(m_ptr + nSize);
				memcpy(m_ptr, sz_new, n_newsize);

				m_curr = (m_ptr + n_newsize);
				//DEBUG_INFO_LIBRARY("buffer align suscess. newsize: %d, oldsize: %d, alignsize: %d",n_newsize , ndata_size, nSize);
				return true;
			}
		}

		bool Expand(unsigned int nSize)
		{
			if(nSize <= m_size)
				return false;

			unsigned int nDataSize = m_curr - m_ptr;
			void *old_p = m_szExpand;
			m_szExpand = (char *)CSimplePool::GetInstance()->Swap(m_ptr, nDataSize, nSize);
			if(m_szExpand != m_ptr)
			{
				//new address
				m_ptr = m_szExpand;
				m_curr = m_ptr + nDataSize;
			}
			if(old_p != m_szExpand)
			{
				CSimplePool::GetInstance()->Free(old_p);
			}

			m_size = nSize;
			return true;
		}

		void Reset(bool bClean = false)
		{
			if(m_szExpand)
			{
				CSimplePool::GetInstance()->Free(m_szExpand);
				m_szExpand = NULL;
			}
			m_ptr = m_curr = GetRawBuffer();
			m_size = DEFSIZE;
			if(bClean)
				memset((void *)m_ptr, 0, m_size);
		}

		bool CopyFrom(void *p, unsigned int nsize)
		{
			if( !Expand(GetDataSize() + nsize) ) return false;
			memcpy(m_ptr, p, nsize);
			m_curr = m_ptr + nsize;
			return true;
		}

		void Append(const void *p, unsigned int nsize)
		{
			if(!p)	return;

			if(GetDataSize() + nsize > m_size)
			{
				if(!Expand(GetDataSize() + nsize))
					return;
			}
			memcpy(m_curr, p, nsize);
			m_curr += nsize;
		}

		void AppendChar(char c)
		{
			char ca = c;
			Append(&ca, 1);
		}

		void AppendStr(const char *p)
		{
			Append(p, strlen(p));
		}

		bool IsDamaged(){ return m_bDamaged; }
		void Discard(){ m_szRawBuff = NULL; };
	private:
		char *m_szRawBuff;
		char *m_szExpand;
		char *m_curr;
		char *m_ptr;
		unsigned int m_size;
		bool m_bDamaged;
	};

NS_CCPY_END

#endif // !__CC_PYTHON_BUFFER_H__
